package com.example.demoes.filter;

import com.example.demoes.entity.RestBean;
import com.example.demoes.utils.Const;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NoArgsConstructor;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@Component
@Order(Const.ORDER_LIMIT)
public class FlowLimitFilter extends HttpFilter {
    @Resource
    StringRedisTemplate template;
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
          String address = request.getRemoteAddr();
          if(this.tryCount(address)){  //计数成功
            chain.doFilter(request, response);
           }else{  //计数失败,实行拦截
             this.writeBlockMessage(response);
          }
    }

    private void writeBlockMessage(HttpServletResponse response) throws IOException{ //访问太快了
          response.setStatus(HttpServletResponse.SC_FORBIDDEN);
          response.setContentType("application/json;charset=utf-8");
          response.getWriter().write(RestBean.forbidden("操作频繁，请稍后重试").asJsonString());
    }

    private boolean tryCount(String ip){
        synchronized (ip.intern()) {
            if (Boolean.TRUE.equals(template.hasKey(Const.FLOW_LIMIT_BLACK + ip))) { //是否有被封禁，相当于是一个读操作
                return false;
            }
            return this.limitPeriodcheck(ip);
        }
    }

    private boolean limitPeriodcheck(String ip){
        if(Boolean.TRUE.equals(template.hasKey(Const.FLOW_LIMIT_COUNTER + ip))){ //是否在限流名单
           long increment = Optional.ofNullable(
                   template.opsForValue().increment(Const.FLOW_LIMIT_COUNTER + ip)
           ).orElse(0L);
           if(increment >10){  //超过请求设置，加入黑名单
               template.opsForValue().set(Const.FLOW_LIMIT_BLACK + ip, "",30, TimeUnit.SECONDS);
               return false;
           }
        }else{ //没有就初始化
            template.opsForValue().set(Const.FLOW_LIMIT_COUNTER + ip, "1",3, TimeUnit.SECONDS);
        }
        return true;
    }
}
